{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 面向对象编程\n",
    "\n",
    "Object Oriented Programming，简称OOP，是一种程序设计思想。OOP把对象作为程序的基本单元，一个对象包含了数据和操作数据的函数。\n",
    "\n",
    "面向过程的程序设计把计算机程序视为一系列的命令集合，即一组函数的顺序执行。为了简化程序设计，面向过程把函数继续切分为子函数，即把大块函数通过切割成小块函数来降低系统的复杂度。\n",
    "\n",
    "而面向对象的程序设计把计算机程序视为一组对象的集合，而每个对象都可以接收其他对象发过来的消息，并处理这些消息，计算机程序的执行就是一系列消息在各个对象之间传递。\n",
    "\n",
    "在Python中，所有数据类型都可以视为对象，当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类（Class）的概念。\n",
    "\n",
    "回顾一下我们已经学了的东西，都是什么类\n",
    "\n",
    ">  \n",
    "Object Oriented Programming, or OOP for short, is a programming idea that treats an object as the basic unit of a program, an object containing data and functions that manipulate that data.\n",
    "\n",
    "> Process-oriented programming treats a computer program as a collection of commands, i.e. a set of functions to be executed sequentially. To simplify programming, process-oriented continues to slice functions into sub-functions, i.e. large blocks of functions are reduced in complexity by cutting them into smaller blocks.\n",
    "\n",
    "> Object-oriented programming, on the other hand, treats a computer program as a collection of objects, and each object can receive messages from other objects and process them; the execution of a computer program is a series of messages being passed between the various objects.\n",
    "\n",
    "> In Python, all data types can be regarded as objects, and it is possible to customise objects. A custom object data type is the concept of a class in object-oriented terms.\n",
    "> Looking back at what we have already learned, what are the classes\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_list的类 <class 'list'>\n",
      "number的类 <class 'int'>\n",
      "str1的类 <class 'str'>\n",
      "dict1的类 <class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "my_list = [1,2,3]\n",
    "print(\"my_list的类\",type(my_list))\n",
    "number = 40\n",
    "print(\"number的类\",type(number))\n",
    "str1 = 'QQ40'\n",
    "print(\"str1的类\",type(str1))\n",
    "dict1 = {'a':1}\n",
    "print(\"dict1的类\",type(dict1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_list.append(32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "40"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "number.numerator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 面向对象设计\n",
    "\n",
    "我们以一个例子来说明<strong>面向过程</strong>和<strong>面向对象</strong>在程序设计流程上的不同之处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "卡罗拉: 12万元\n"
     ]
    }
   ],
   "source": [
    "# Suppose we want to process a car transaction. \n",
    "# To represent the properties of a car, a procedure-oriented program can be represented by a dict：\n",
    "car1 = {'price':12, 'name':'BYD',\"owner\":\"Jack\"}\n",
    "car2 = {'price':40, 'name':'BMW',\"owner\":\"Alex\"}\n",
    "\n",
    "def print_info(std):\n",
    "    print('%s: %s' % (std['name'],std['price']))\n",
    "\n",
    "print_info(car1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果采用面向对象的程序设计思想，我们首选思考的不是程序的执行流程，而是Car这种数据类型应该被视为一个对象，这个对象拥有属性（Property）。如果要打印一辆车的信息，首先必须创建出这个车对应的对象，然后，给对象发一个print_info消息，让对象自己把自己的数据打印出来。\n",
    "\n",
    "> If object-oriented programming is used, the first thing to think about is not the execution process of the program, but the fact that a data type like Car should be considered as an object, which has properties. If you want to print information about a car, you must first create the object that corresponds to that car and then, by sending a print_info message to the object, ask the object to print out its own data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car(object): # object is the most base class\n",
    "    \n",
    "    def __init__(self, name, price,owner):\n",
    "        # Functions that are placed inside a class are called methods\n",
    "        # Define the properties of this class\n",
    "        self.price = price\n",
    "        self.name = name\n",
    "        self.owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        # To define a method, it is the same as a normal function except that the first argument is self.\n",
    "        # To call a method, simply call it directly on the instance variable, except that self does not need to be passed, \n",
    "        # and the other parameters are passed in as normal:\n",
    "        print('{}的{}: {}'.format(self.owner, self.name, self.price))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "给对象发消息实际上就是调用对象对应的关联函数，我们称之为对象的方法（Method）。面向对象的程序写出来就像这样：\n",
    "\n",
    "> Sending a message to an object is actually a call to the object's corresponding associated function, which we call the object's Method. An object-oriented program is written like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小李的卡罗拉: 12万元\n"
     ]
    }
   ],
   "source": [
    "c1 = Car('BMW', 120, 'Tim')\n",
    "c1.print_info()   # 这些数据和逻辑被“封装”起来了，调用很容易，但却不用知道内部实现的细节。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面向对象的设计思想是从自然界中来的，因为在自然界中，类（Class）和实例（Instance）的概念是很自然的。Class是一种抽象概念，比如我们定义的Class——Car，是指车这个概念，而实例（Instance）则是一个个具体的车，比如，Luc的车\n",
    "\n",
    "> Object-oriented design thinking comes from nature, where the concept of a class (Class) and an instance (Instance) is natural. class is an abstract concept, for example, we define Class - Car, which refers to the concept of a car, while an instance ( Instance) is an individual concrete car, for example, Luc's car"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "class后面紧接着是类名，即Car，类名通常是大写开头的单词，紧接着是(object)，表示该类是从哪个类继承下来的，继承的概念我们后面再讲，通常，如果没有合适的继承类，就使用object类，这是所有类最终都会继承的类。\n",
    "\n",
    "> The class name is immediately followed by the class name, Car, which is usually a word starting with capital letters, followed immediately by (object), indicating the class from which the class is inherited, the concept of inheritance we will talk about later, usually, if there is no suitable inherited class, use the object class, which is the class that all classes will eventually inherit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个实例\n",
    "my_car = Car('五菱宏光', 8, '刘毅')  #my_car是变量，自己定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Car object at 0x0000000004EE0A90>\n"
     ]
    }
   ],
   "source": [
    "print(my_car)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，变量my_car指向的就是一个Car的实例，后面的0x7f2d08585b70是内存地址，每个object的地址都不一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "刘毅的宝马: 8万元\n",
      "刘毅的宝马: -2万元\n"
     ]
    }
   ],
   "source": [
    "# 自由为对象赋值\n",
    "my_car.print_info() # 修改前\n",
    "my_car.name = \"宝马\"\n",
    "my_car.price = -2\n",
    "my_car.print_info() # 修改后"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类可以起到模板的作用，因此，可以在创建实例的时候，把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法，在创建实例的时候，就把name, price等属性绑上去：\n",
    "\n",
    "<strong>特殊方法“__init__”前后分别有两个下划线！！！</strong>\n",
    "\n",
    "注意到__init__方法的第一个参数永远是self，表示创建的实例本身，因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。\n",
    "\n",
    "有了__init__方法，在创建实例的时候，就不能传入空的参数了，必须传入与__init__方法匹配的参数，但self不需要传，Python解释器自己会把实例变量传进去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "__init__() missing 1 required positional argument: 'owner'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-21-91191b41c12b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 创建实例 缺失参数会报错\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mmy_car\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mCar\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'五菱宏光'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m8\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: __init__() missing 1 required positional argument: 'owner'"
     ]
    }
   ],
   "source": [
    "# 创建实例 缺失参数会报错\n",
    "my_car = Car('五菱宏光', 8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习1 \n",
    "\n",
    "写一个学生类型，包含姓名(name),年龄(age),专业(profession)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小都年龄是18岁，专业是计算机系统\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, age, profession):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        self.profession = profession\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s年龄是%s岁，专业是%s' % (self.name, self.age, self.profession))\n",
    "\n",
    "s = Student('小都',18,'计算机系统')\n",
    "# 输出学生信息\n",
    "s.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 访问限制\n",
    "从前面Car类的定义来看，外部代码还是可以自由地修改一个实例的name、price等属性\n",
    "\n",
    "如果要让内部属性不被外部访问，可以把属性的名称前加上两个下划线__，\n",
    "\n",
    "在Python中，实例的变量名如果以__开头，就变成了一个私有变量（private），\n",
    "\n",
    "只有内部可以访问，外部不能访问，所以，我们把Car类改一改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_2(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.__owner, self.__name, self.__price))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "直接获取 本田\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Car_2' object has no attribute '__name'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-27-6b570ec1327f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"直接获取\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmy_car1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"报错\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmy_car2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 'Car_2' object has no attribute '__name'\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m \u001b[1;31m# 这样就确保了外部代码不能随意修改对象内部的状态，\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[1;31m# 这样通过访问限制的保护，代码更加健壮。\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mAttributeError\u001b[0m: 'Car_2' object has no attribute '__name'"
     ]
    }
   ],
   "source": [
    "my_car1 = Car('本田', 12, '刘毅')\n",
    "my_car2 = Car_2('五菱宏光', 8, '初言')\n",
    "\n",
    "print(\"直接获取\", my_car1.name)\n",
    "print(\"报错\", my_car2.__name) # 'Car_2' object has no attribute '__name'\n",
    "# 这样就确保了外部代码不能随意修改对象内部的状态，\n",
    "# 这样通过访问限制的保护，代码更加健壮。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初言的五菱宏光: 8万元\n"
     ]
    }
   ],
   "source": [
    "my_car2.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 如果外部代码要获取name,price怎么办？可以给Car类增加get_name和get_price这样的方法\n",
    "2. 如果又要允许外部代码修改score怎么办？可以再给Student类增加set_score方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_3(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.owner, self.name, self.price))\n",
    "    \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "\n",
    "    def get_price(self):\n",
    "        return self.__price\n",
    "    \n",
    "    def set_price(self, price):\n",
    "        self.__price = price"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你也许会问，原先那种直接通过my_car.price = 20也可以修改啊，为什么要定义一个方法大费周折？因为在方法中，可以对参数做检查，避免传入无效的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_4(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (\n",
    "            self.__owner, self.__name, self.__price))\n",
    "    \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "\n",
    "    def get_price(self):\n",
    "        return self.__price\n",
    "    \n",
    "    def set_price(self, price):\n",
    "        if 2 <= price:\n",
    "            self.__price = price\n",
    "        else:\n",
    "            print('设置无效：价格不能为负数')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "设置无效：价格不能为负数\n"
     ]
    }
   ],
   "source": [
    "my_car4 = Car_4('五菱宏光', 8, 'Louis')\n",
    "my_car4.set_price(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Louis的五菱宏光: 12万元\n"
     ]
    }
   ],
   "source": [
    "my_car4.set_price(12)\n",
    "my_car4.print_info()"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "注意\n",
    "\n",
    "在Python中，变量名类似__xxx__的，也就是以双下划线开头，并且以双下划线结尾的，是特殊变量，特殊变量是可以直接访问的，不是private变量，所以，不能用__name__、__score__这样的变量名。\n",
    "\n",
    "有些时候，你会看到以一个下划线开头的实例变量名，比如_name，这样的实例变量外部是可以访问的，但是，按照约定俗成的规定，当你看到这样的变量时，意思就是，“虽然我可以被访问，但是，请把我视为私有变量，不要随意访问”。\n",
    "\n",
    "双下划线开头的实例变量是不是一定不能从外部访问呢？其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Car__name，所以，仍然可以通过_Car__name来访问__name变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Louis的路虎: 8万元\n"
     ]
    }
   ],
   "source": [
    "my_car4._Car_4__name = \"路虎\"\n",
    "my_car4.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是强烈建议你不要这么干，因为不同版本的Python解释器可能会把__name改成不同的变量名。\n",
    "\n",
    "总的来说就是，Python本身没有任何机制阻止你干坏事，一切全靠自觉。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习2\n",
    "\n",
    "在上面的Car_4对象基础上添加　《颜色:color》　属性， 用get_color()获取颜色，set_color设置颜色"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_5(Car_4):\n",
    "    # Car_4 代替 object，代表Car_5继承Car_4特征\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "        self.__color = None\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s%s: %s万元' % (\n",
    "            self.__owner, self.__color, self.__name, self.__price))\n",
    "    \n",
    "    # 添加颜色参数\n",
    "    def get_color(self):\n",
    "        return self.__color\n",
    "    \n",
    "    def set_color(self, color):\n",
    "        self.__color = color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "未添加颜色: None\n",
      "添加颜色后: 红色\n",
      "小法的红色雷克萨斯: 18万元\n"
     ]
    }
   ],
   "source": [
    "my_car5 = Car_5('雷克萨斯', 18, '小法')\n",
    "print('未添加颜色:', my_car5.get_color())\n",
    "my_car5.set_color('红色')\n",
    "print('添加颜色后:', my_car5.get_color())\n",
    "my_car5.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 继承与多态\n",
    "\n",
    "在OOP程序设计中，当我们定义一个class的时候，可以从某个现有的class继承，新的class称为子类（Subclass），而被继承的class称为基类、父类或超类（Base class、Super class）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal(object):\n",
    "    def run(self):\n",
    "        print('Animal is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 当我们需要编写Dog和Cat类时，就可以直接从Animal类继承：\n",
    "class Dog(Animal):\n",
    "    pass\n",
    "\n",
    "class Cat(Animal):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于Dog来说，Animal就是它的父类，对于Animal来说，Dog就是它的子类。Cat和Dog类似。\n",
    "\n",
    "继承有什么好处？最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法，因此，Dog和Cat作为它的子类，什么事也没干，就自动拥有了run()方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running...\n",
      "Animal is running...\n"
     ]
    }
   ],
   "source": [
    "dog = Dog()\n",
    "dog.run()\n",
    "\n",
    "cat = Cat()\n",
    "cat.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "继承的第二个好处需要我们对代码做一点改进。\n",
    "\n",
    "你看到了，无论是Dog还是Cat，它们run()的时候，显示的都是Animal is running...，\n",
    "\n",
    "符合逻辑的做法是分别显示Dog is running...和Cat is running...，\n",
    "\n",
    "因此，对Dog和Cat类改进如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Dog is running...')\n",
    "\n",
    "class Cat(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Cat is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n",
      "Cat is running...\n"
     ]
    }
   ],
   "source": [
    "dog = Dog()\n",
    "dog.run()\n",
    "\n",
    "cat = Cat()\n",
    "cat.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当子类和父类都存在相同的run()方法时，我们说，子类的run()覆盖了父类的run()，在代码运行的时候，总是会调用子类的run()。这样，我们就获得了继承的另一个好处：多态\n",
    "\n",
    "### 多态\n",
    "\n",
    "要理解什么是多态，我们首先要对数据类型再作一点说明。当我们定义一个class的时候，我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型，比如str、list、dict没什么两样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "list类型 <class 'list'> True\n",
      "Animal类型 <class '__main__.Animal'> True\n",
      "Dog类型 <class '__main__.Dog'> True\n"
     ]
    }
   ],
   "source": [
    "a = list() # a是list类型\n",
    "b = Animal() # b是Animal类型\n",
    "c = Dog() # c是Dog类型\n",
    "\n",
    "#　判断一个变量是否是某个类型可以用isinstance()判断：\n",
    "print(\"list类型\", type(a), isinstance(a, list))\n",
    "print(\"Animal类型\", type(b), isinstance(b, Animal))\n",
    "print(\"Dog类型\", type(c), isinstance(c, Dog))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 尝试一下这个，Ｄｏｇ　是否也是　Animal　类型\n",
    "isinstance(c, Animal)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看来c不仅仅是Dog，c还是Animal！\n",
    "\n",
    "不过仔细想想，这是有道理的，因为Dog是从Animal继承下来的，当我们创建了一个Dog的实例c时，我们认为c的数据类型是Dog没错，但c同时也是Animal也没错，Dog本来就是Animal的一种！\n",
    "\n",
    "所以，在继承关系中，如果一个实例的数据类型是某个子类，那它的数据类型也可以被看做是父类。但是，反过来就不行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Animal 不是　Ｄｏｇ　类型\n",
    "isinstance(b,Dog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "继承还可以一级一级地继承下来，就好比从爷爷到爸爸、再到儿子这样的关系。而任何类，最终都可以追溯到根类object，这些继承关系看上去就像一颗倒着的树。比如如下的继承树：\n",
    "\n",
    "object\n",
    "\n",
    "   |-->Animal\n",
    "  \n",
    "        |-->Dog\n",
    "        \n",
    "        |-->Cat\n",
    "        \n",
    "   |-->Plant\n",
    "  \n",
    "        |-->Tree\n",
    "        \n",
    "        |-->Flower\n",
    "       "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 静态语言 vs 动态语言\n",
    "\n",
    "对于静态语言（例如Java）来说，如果需要传入Animal类型，则传入的对象必须是Animal类型或者它的子类，否则，将无法调用run()方法。\n",
    "\n",
    "对于Python这样的动态语言来说，则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car(object):\n",
    "    def run(self):\n",
    "        print('Car run...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这就是动态语言的“鸭子类型”，它并不要求严格的继承体系，一个对象只要“看起来像鸭子，走起路来像鸭子”，那它就可以被看做是鸭子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_twice(animal):\n",
    "    animal.run()\n",
    "    animal.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n",
      "Dog is running...\n",
      "Cat is running...\n",
      "Cat is running...\n",
      "Car run...\n",
      "Car run...\n"
     ]
    }
   ],
   "source": [
    "run_twice(Dog())\n",
    "run_twice(Cat())\n",
    "run_twice(Car())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习3\n",
    "\n",
    "设计一个马(horse),它继承animal类，改写run方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Horse run...\n"
     ]
    }
   ],
   "source": [
    "# 练习：设计一个马(horse),它继承animal类，改写run方法\n",
    "\n",
    "class Horse(Animal):\n",
    "    \n",
    "    def run(self):\n",
    "        print('Horse run...')\n",
    "\n",
    "# 创建实例\n",
    "\n",
    "h = Horse()\n",
    "\n",
    "h.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 扩展知识\n",
    "\n",
    "### __slots__\n",
    "要限制实例的属性\n",
    "\n",
    "比如，只允许对Student实例添加name和age属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom\n"
     ]
    }
   ],
   "source": [
    "# 正常情况下，当我们定义了一个class，创建了一个class的实例后，\n",
    "# 我们可以给该实例绑定任何属性和方法，这就是动态语言的灵活性。先定义class\n",
    "s = Student()\n",
    "s.name = 'Tom'\n",
    "print(s.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为了达到限制的目的，Python允许在定义class的时候，\n",
    "# 定义一个特殊的__slots__变量，来限制该class实例能添加的属性：\n",
    "\n",
    "class Student_2(object):\n",
    "    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student_2' object has no attribute 'score'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-61-74b7accd7276>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStudent_2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscore\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m99\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscore\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student_2' object has no attribute 'score'"
     ]
    }
   ],
   "source": [
    "s = Student_2()\n",
    "s.score = 99\n",
    "print(s.score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 装饰器（decorator）\n",
    "\n",
    "python装饰器是Python中比较难理解的概念。简单来说装饰器是用来对某个函数增加功能的，但又不需要改变函数提的内容,看例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "执行myDectoation函数2\n",
      "执行myDectoation函数1\n",
      "正在执行内部函数1\n",
      "正在执行内部函数2\n",
      "------结果-------\n",
      "佛山欢迎您 广东欢迎你 中国欢迎你\n"
     ]
    }
   ],
   "source": [
    "def myDectoration1(func1):\n",
    "    print(\"执行myDectoation函数1\")\n",
    "    def inner1(*args):\n",
    "        print(\"正在执行内部函数1\")\n",
    "        return func1(*args)+\" 中国欢迎你\"\n",
    "    return inner1\n",
    "\n",
    "def myDectoration2(func2):\n",
    "    print(\"执行myDectoation函数2\")\n",
    "    def inner2(*args):\n",
    "        print(\"正在执行内部函数2\")\n",
    "        return func2(*args)+\" 广东欢迎你\"\n",
    "    return inner2\n",
    "\n",
    "\n",
    "@myDectoration1\n",
    "@myDectoration2\n",
    "def printMessage(name):\n",
    "    return \"%s欢迎您\"%name\n",
    "\n",
    "messgae = printMessage(\"佛山\")\n",
    "print(\"------结果-------\")\n",
    "print(messgae)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于函数也是一个对象，而且函数对象可以被赋值给变量，所以，通过变量也能调用该函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    @property\n",
    "    def score(self):\n",
    "        return self._score\n",
    "\n",
    "    @score.setter\n",
    "    def score(self, value):\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('score must be an integer!')\n",
    "        if value < 0 or value > 100:\n",
    "            print(\"score must between 0 ~ 100!\")\n",
    "            #raise ValueError('score must between 0 ~ 100!')\n",
    "            return\n",
    "        self._score = value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神奇的@property，我们在对实例属性操作的时候，就知道该属性很可能不是直接暴露的，而是通过getter和setter方法来实现的。\n",
    "\n",
    "还可以定义只读属性，只定义getter方法，不定义setter方法就是一个只读属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "60\n",
      "score must between 0 ~ 100!\n",
      "60\n"
     ]
    }
   ],
   "source": [
    "s = Student()\n",
    "s.score = 60\n",
    "print(s.score)\n",
    "s.score = 999\n",
    "print(s.score)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "## 只定义getter方法，不定义setter方法就是一个只读属性：\n",
    "class Student(object):\n",
    "\n",
    "    @property\n",
    "    def birth(self):\n",
    "        return self._birth\n",
    "\n",
    "    @birth.setter\n",
    "    def birth(self, value):\n",
    "        self._birth = value\n",
    "\n",
    "    @property\n",
    "    def age(self):\n",
    "        return 2018 - self._birth"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "s = Student()\n",
    "#s.age = 10\n",
    "s.birth = 1993\n",
    "print(s.age)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的birth是可读写属性，而age就是一个只读属性，因为age可以根据birth和当前时间计算出来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习4\n",
    "\n",
    "Ｃａｒ 对象中加上重量(weight)，出厂日期(created)两个私有属性，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 练习－　　Ｃａｒ 对象中加上重量(weight)，出厂日期(created)两个私有属性，\n",
    "class Car_exec(object):\n",
    "    \n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "    \n",
    "    @property\n",
    "    def weight(self):\n",
    "        return self._weight\n",
    "    \n",
    "    @weight.setter\n",
    "    def weight(self, value):\n",
    "        self._weight = value\n",
    "    \n",
    "    @property\n",
    "    def created(self):\n",
    "        return self._created\n",
    "    \n",
    "    @created.setter\n",
    "    def created(self, value):\n",
    "        self._created = value\n",
    "    \n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.owner, self.name, self.price))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "2016\n"
     ]
    }
   ],
   "source": [
    "my_car = Car_exec('雷克萨斯', 18, '小法')\n",
    "my_car.weight = 10\n",
    "print(my_car.weight)\n",
    "my_car.created = 2016\n",
    "print(my_car.created)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
